--[[
    ElementXml: xmlnode => ElementXml
    完成xml解析
]]
local Style = _ENV.import("./Style/Style.lua");
local StyleSheet = _ENV.import("./Style/StyleSheet.lua");

local ElementXml = commonlib.inherit(commonlib.ToolBase);

ElementXml:Property("ClassName");                   -- 类名
ElementXml:Property("Parent");                      -- 父元素
ElementXml:Property("TagName");                     -- 标签名
ElementXml:Property("XmlNode");                     -- 节点原始数据
ElementXml:Property("Hover", false, "IsHover");     -- 是否鼠标悬浮
ElementXml:Property("Focus", false, "IsFocus");     -- 是否聚焦

-- 构造函数
function ElementXml:ctor()
    self:SetClassName("ElementXml");

    -- 子元素列表
    self.m_childrens = {};

    -- 属性集
    self.m_attrs = {};

    -- 样式表
    self.m_style_sheet = nil;

    -- 当前样式
    self.m_style = nil;
end

-- 初始化函数
function ElementXml:Init(xmlnode, parent)
    self.m_attrs = commonlib.copy(xmlnode.attr or {});
    self.m_style = Style:new():Init();

    self:SetParent(parent);
    self:SetTagName(xmlnode.name);
    self:SetXmlNode(xmlnode);

    for _, subnode in ipairs(xmlnode) do
        local subElement = self:CreateElement(subnode);
        if (subElement ~= nil) then
            self:AddChildren(subElement:Init(subnode, self));
        end
    end

    return self;
end

-- 添加子元素
function ElementXml:AddChildren(children)
    if (not children) then return end
    table.insert(self.m_childrens, children);
end

-- 判断子元素是否存在
function ElementXml:IsExistChildren(children)
    if (not children) then return false end
    for index, child in ipairs(self.m_childrens) do
        if (child == children) then return true, index end
    end
    return false;
end

-- 获得子元素
function ElementXml:GetChildren(index)
    return self.m_childrens[index];
end

-- 移除子元素
function ElementXml:RemoveChildren(children)
    local exist, index = self:IsExistChildren(children);
    if (exist) then table.remove(self.m_childrens, index) end
end

-- 上一个兄弟元素
function ElementXml:GetPrevSibling()
    local parent = self:GetParent();
    if (not parent) then return end
    local prev_sibling = nil;
    for _, element in ipairs(parent.m_childrens) do
        if (element == self) then return prev_sibling end
        prev_sibling = element;
    end
end

-- 下一个兄弟元素
function ElementXml:GetNextSibling()
    local parent = self:GetParent();
    if (not parent) then return end
    local next_sibling = nil;
    for i = #parent.m_childrens, 1, -1 do
        local element = parent.m_childrens[i];
        if (element == self) then return next_sibling end
        next_sibling = element;
    end
end

-- 获取元素位置
function ElementXml:GetChildrenIndex(target_children)
    for index, children in ipairs(self.m_childrens) do
        if (children == target_children) then return index end
    end
    return 0;
end

-- 获取元素位置
function ElementXml:GetIndexInParent()
    local parent = self:GetParent();
    if (not parent) then return 0 end
    return parent:GetChildrenIndex(self);
end

-- 默认渲染序 z-index 由小到大迭代
function ElementXml:ChildrenIterator()
    local index = 0;
    return function()
        index = index + 1;
        return self.m_childrens[index];
    end
end

-- 创建元素
function ElementXml:CreateElement(xmlnode)
    local text = commonlib.trim(xmlnode.text or "");
    local tagname = xmlnode.name or "";
    if (tagname == "" and text == "") then return nil end
    local class = self:GetElementClassByTag(tagname == "" and "text" or tagname);
    return class and class:new();
end

-- 获得元素类表
function ElementXml:GetElementClassMap()
    self.m_element_class_map = self.m_element_class_map or {};
    return self.m_element_class_map;
end

-- 注册元素类
function ElementXml:RegisterElementClass(tagname, cls)
    (self:GetElementClassMap())[string.lower(tagname)] = cls;
end

-- 获取元素类
function ElementXml:GetElementClassByTag(tagname)
    return (self:GetElementClassMap())[string.lower(tagname)];
end

-- 获取属性值
function ElementXml:GetAttrValue(attrName, defaultValue)
    local attrValue = self.m_attrs[attrName];
    if (attrValue == nil) then return defaultValue end
    return attrValue;
end

-- 获取数字属性值
function ElementXml:GetAttrNumberValue(attrName, defaultValue)
    return tonumber(self:GetAttrValue(attrName)) or defaultValue or 0;
end

-- 获取字符串属性值
function ElementXml:GetAttrStringValue(attrName, defaultValue)
    local value = self:GetAttrValue(attrName)
    return (value ~= nil and tostring(value) or defaultValue) or "";
end

-- 获取布尔属性值
function ElementXml:GetAttrBoolValue(attrName, defaultValue)
    local value = self:GetAttrValue(attrName);
    if (type(value) == "boolean") then return value end
    if (type(value) ~= "string") then return defaultValue end
    return value == "true";
end

-- 获取表属性值
function ElementXml:GetAttrTableValue(attrName, defaultValue)
    local value = self:GetAttrValue(attrName);
    return (type(value) == "table" and value or defaultValue) or {};
end
-- 获取函数属性
function ElementXml:GetAttrFunctionValue(attrName, defaultValue)
    local value = self:GetAttrValue(attrName);
    if (type(value) == "function") then return value end
    if (type(value) == "string") then
        value = commonlib.loadstring(value, self:GetG());
        self:SetAttrValue(attrName, value); -- 编译缓存
        return value or defaultValue;
    end
    return defaultValue;
end

-- 获取监控对象
function ElementXml:GetWatchObject()
    local parent = self:GetParent();
    return parent and parent:GetWatchObject();
end

-- 获取元素上值
function ElementXml:GetG()
    local watch_object = self:GetWatchObject();
    return watch_object and watch_object:GetWatchData() or setmetatable({}, {__index = _G});
end

-- 获取样式值
function ElementXml:GetStyleValue(styleName, defaultStyleValue)
    return self:GetStyle():GetStyleValue(styleName) or defaultStyleValue;
end

-- 加载样式表
function ElementXml:LoadStyleSheet(xmlnode)
    self.m_style_sheet = StyleSheet:new():Init(self:GetXmlNodeText(xmlnode));
end

-- 获取样式表
function ElementXml:GetStyleSheet()
    if (self.m_style_sheet) then return self.m_style_sheet end
    local parent = self:GetParent();
    return parent and parent:GetStyleSheet();
end

-- 加载脚本
function ElementXml:LoadScript(xmlnode)
    commonlib.dostring(self:GetXmlNodeText(xmlnode), self:GetG());
end

-- 获取xmlnode文本
function ElementXml:GetXmlNodeText(xmlnode)
    xmlnode = xmlnode or self:GetXmlNode();
    local text = "";
    for _, subnode in ipairs(xmlnode) do
        text = text .. "\n" .. subnode.text;
    end
    return text;
end

-- 获取样式选择器列表
function ElementXml:GetStyleSelectors()
    local selectors = {};
    local class_name_list = commonlib.split(self:GetAttrStringValue("class"));
    -- 通配符*选择器
    table.insert(selectors, "*");

    -- 便签选择器
    table.insert(selectors, self:GetTagName());

    -- 类选择器
    for _, class_name in ipairs(class_name_list) do
        table.insert(selectors, "." .. class_name);
    end

    -- ID选择器
    local id = self:GetAttrValue("id");
    if (id and id ~= "") then table.insert(selectors, "#" .. tostring(id)) end

    -- 伪类选择器
    local size = #selectors;
    if (self:IsHover()) then
        for i = 1, size do
            table.insert(selectors, selectors[i] .. ":hover");
        end
    end
    if (self:IsFocus()) then
        for i = 1, size do
            table.insert(selectors, selectors[i] .. ":focus");
        end
    end
    return selectors;
end

-- 获取样式
function ElementXml:GetStyle()
    return self.m_style;
end

-- 生效元素样式前回调
function ElementXml:OnBeforeUpdateStyle()
    -- 清空旧样式
    self:GetStyle():Clear();
    -- 追加默认样式
end

-- 更新子元素样式
function ElementXml:OnUpdateChildrenStyle()
    for children in self:ChildrenIterator() do
        children:UpdateStyle();
    end
end

-- 生效元素样式
function ElementXml:UpdateStyle()
    self:OnBeforeUpdateStyle();

    self:GetStyleSheet():ApplyElementStyle(self);

    self:OnUpdateChildrenStyle();

    self:OnAfterUpdateStyle();
end

-- 生效元素样式后
function ElementXml:OnAfterUpdateStyle()
    -- 追加内联样式
    self:GetStyle():AppendStyleText(self:GetAttrValue("style"));
end

return ElementXml;